package org.webscale.util.s3;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.model.S3Bucket;
import org.jets3t.service.model.S3Object;
import org.jets3t.service.security.AWSCredentials;
//import org.webscale.util.AWSCredentials;
import org.webscale.util.AWSCredential;



/**
 * An Object similar to java.io.File
 */
public class S3File implements Comparable<S3File> , Serializable {
	Logger log = Logger.getLogger(S3File.class);

	private String bucketName = null;
	private String path ;
	private AWSCredential credential = null;
	private S3Object s3Object = null;
	private S3Bucket s3Bucket = null;
	private boolean fileExists = false;
	private boolean isDirectory = false;
	static S3Service s3Service = null;
	
	 S3Service getS3Service(AWSCredential cred) throws S3Exception{
		if(s3Service == null){
			AWSCredentials awsCredentials =  new AWSCredentials(cred.getAwsKey() , cred.getAwsSecretKey());
			try {
				s3Service = new RestS3Service(awsCredentials);
			} catch (S3ServiceException e) {
				log.error(e);
				throw new S3Exception(e);
				
			}
		}
		return s3Service;
	}
	
	public S3File(AWSCredential cred , String bucket){
		super();
		this.bucketName = bucket;
		this.credential = cred;
		s3Bucket = findBucket(bucketName);
	}
	
	public S3File(AWSCredential cred , String bucket , File localFile) throws FileNotFoundException{
		super();
		this.bucketName = bucket;
		this.credential = cred;
		s3Bucket = findBucket(bucketName);
		s3Object = new S3Object(s3Bucket , localFile);
		
	}
	
	
	public S3File(AWSCredential cred ,String bucket , String pathName){
		this(cred ,bucket);
		
		try {
			s3Object = getS3Service(credential).getObjectDetails(s3Bucket, pathName);
			fileExists = true;
			isDirectory = false;
			path = pathName;
		} catch (S3ServiceException e) {
			fileExists = false;
			//log.debug(e);
			try {
				s3Object = getS3Service(credential).getObjectDetails(s3Bucket, pathName +"_$folder$");
				isDirectory = true;
				fileExists = true;
				path = pathName +"_$folder$";
			} catch (S3ServiceException e1) {
				fileExists = false;
				isDirectory = false;
				//log.debug(e);
			}
		}
	}
	
	public S3File(S3File parent , String child){
		this(parent.credential , parent.getBucketName());
		String parentPath = parent.getAbsolutePath();
		String path = parentPath.substring(0 , parentPath.indexOf("_$folder$"));
		String newPath = path + "/" + child;

		try {
			s3Object = getS3Service(credential).getObjectDetails(s3Bucket, newPath);
			fileExists = true;
			isDirectory = false;
			path = newPath;
		} catch (S3ServiceException e) {
			fileExists = false;
			log.debug(e);
			try {
				s3Object = getS3Service(credential).getObjectDetails(s3Bucket, newPath +"_$folder$");
				isDirectory = true;
				fileExists = true;
				path = newPath +"_$folder$" ;
			} catch (S3ServiceException e1) {
				fileExists = false;
				isDirectory = false;
				log.debug(e);
			}
		}
	}

	public S3File(URI uri){
		throw new UnsupportedOperationException(); 
	}
	
	public S3Bucket getBucket(){
		return s3Bucket;
	}
	
	public S3Object getS3Object(){
		return s3Object;
	}
	
	public String getBucketName() {
		return bucketName;
	}
	public boolean createNewFile() {
		s3Object = new S3Object(s3Bucket , path);
		try {
			s3Object = getS3Service(credential).putObject(s3Bucket, s3Object);
			fileExists = true;
			return true;
		}  catch (S3ServiceException e) {
			log.error(e);
			return false;
		}
	}
	public boolean exists(){
		return fileExists;
	}
	
	public boolean isDirectory(){
		return isDirectory;
	}
	
	public boolean canRead(){
	   throw new UnsupportedOperationException(); //TODO: Implement ACL stuff 
	}
	
	public boolean canWrite(){
		 throw new UnsupportedOperationException();  //TODO: Implement ACL stuff 
	}
	
	public int compareTo(File file){
		throw new UnsupportedOperationException(); 
	}
	
	public int compareTo(S3File o) {
		throw new UnsupportedOperationException(); 
	}
	
	public String getAbsolutePath(){
		String path = "/";
		if(s3Object != null)
			path = s3Object.getKey();
		return path;
	}
	
	
	public String[] listFiles(S3FileNameFilter filter){
		ArrayList<String> files = new ArrayList<String>();
		 String dirPath = getParentPath(this.getAbsolutePath());
		 dirPath = stripFolderPathFromName(dirPath); 
	     S3Object[] s3object;
		try {
			s3object = listFilesUnderBucket(bucketName, dirPath);
			
		} catch (S3ServiceException e) {
			 log.error("Error", e);
			 throw new S3Exception(e);
		}
		for(int i=0 ; i < s3object.length ; i++){
		  if(filter.accept(s3object[i].getKey())){
			  files.add(s3object[i].getKey());
		  }
		 }
		 String [] fileA = new String[files.size()];  
	     return files.toArray(fileA);
	}
    
	
	private String stripFolderPathFromName(String folderName){
		String str = folderName;
		int index = folderName.indexOf("_$folder$");
		if(index > 0 ){
			str = folderName.substring(0 , index);
		}
		return str;
	}
	
	/**
	 * @param filter
	 * @return
	 */
	public S3File[] listFiles(S3FileFilter filter) {
		ArrayList<S3File> files = new ArrayList<S3File>();
		 String dirPath = getParentPath(this.getAbsolutePath());
		 dirPath = stripFolderPathFromName(dirPath);
		 log.debug("absolute path " + dirPath);
	     S3Object[] s3object;
		try {
			s3object = listFilesUnderBucket(bucketName, dirPath);
		} catch (S3ServiceException e) {
			 log.error("Error", e);
			 throw new S3Exception(e);
		}
	    for(int i=0 ; i < s3object.length ; i++){
	    	 S3File file = new S3File(credential , this.bucketName , s3object[i].getKey());
	    	 log.debug("S3 File created for " + file.getAbsolutePath());
	    	if(filter.accept(file)){
	    		files.add(file);
	    	}
	     }
	   S3File [] fileA = new S3File[files.size()];  
       return files.toArray(fileA);
	}
	
	private String  getParentPath(String path){
		 String dirPath = path;
	      if(!this.isDirectory()){
	    	  int index = dirPath.lastIndexOf('/');
	    	  if(index >0){
	    		  dirPath = dirPath.substring(0 , index);
	    	  }
	      }
	      return dirPath;
	}
	
	private S3Bucket findBucket(String bucketName) {
		S3Bucket[] bucs;
		try {
			bucs = getS3Service(credential).listAllBuckets();
			S3Bucket thebucket = null ;
			for(int i=0 ; i < bucs.length ; i++){
				String name = bucs[i].getName();
				if(bucketName.equals(name))
					thebucket = bucs[i];
			}
			return thebucket;
		} catch (S3ServiceException e) {
			throw new S3Exception(e);
		}
	}	
	
	private S3Object[] listFilesUnderBucket(String bucketName , String fullFolder) throws S3ServiceException{

		S3Bucket thebucket =  findBucket(bucketName) ;
		String prefix =null;
		String suffix = "/";
		if(fullFolder.equals("root"))
		{
			prefix = null;
		}else
		{
			prefix = fullFolder + "/";
		}
		log.debug("bucket " + thebucket + "\n pre = " + prefix + " \nsuffix " + suffix);
		S3Object[] objects = getS3Service(credential).listObjects(thebucket, prefix, suffix);
		
		log.debug("Size = " +objects.length +" "  );
		return objects;
	}

}

